<?php

// ---------------------------------------------------------------------------------------
//
// rFactor Server Query PHP Class by Rene - rene@wiesental-racing.de
//                                          www.wiesental-racing.de
//
// Das Script kann gerne jederzeit ge�ndert oder erweitert werden. Vorrausetzung
// daf�r ist, dass dieser komplette Kommentarblock und die beiliegende ReadMe erhalten bleiben.
// Es w�re sch�n, wenn ihr eigene Erweiterungen/Verbesserungen an mich sendet, damit ich
// sie zentral aufnehmen kann und alle etwas davon haben. (9.6.2006)
//
// Der Code wurde unter PHP 4.2.2 und 4.3.11 getestet, m�sste aber auch unter PHP 5 lauff�hig sein.
// F�r Fehler irgendeiner Art die durch die Benutzung des Code veruracht werden �bernehme ich
// selbstverst�ndlich keinerlei Haftung.
//
// ---------------------------------------------------------------------------------------


define("SESSION_UNDEF",     0);
define("SESSION_PRACTICE1", 1);
define("SESSION_PRACTICE2", 2);
define("SESSION_PRACTICE3", 3);
define("SESSION_PRACTICE4", 4);
define("SESSION_QUALIFY",   5);
define("SESSION_WARMUP",    6);
define("SESSION_RACE",      7);


if (!class_exists("rFQuery"))
{

class rFQuery
{
	var $ip;						// IP address of server
	var $port;					// Server query port
	
	var $read_blocked;		// Determines whether to use blocking socket or not
	
	var $serveronline;		// Indicates whether or not the server is online
	
	var $gameversion;			// Version of rFactor
	var $servername;			// Name of the server
	var $servermodt;			// Server motto of the day
	var $series;				// Name of the race series currently running
	var $track;					// Name of the track currently loaded
	var $session;				// Session information (Practice, Qualifying, Warmup, Race)
	var $session_time;		// Time left for the current session (sec)
	var $curplayers;			// Actual number of drivers on the server
	var $maxplayers;			// Maximum allowed number of drivers on the server
	var $numaiplayers;		// Number of AI drivers
	var $damagemult;			// Damage multiplier (%)
	var $datarate;				// Server data rate (kbps)

        var $hasextrainfo = false;                      // Does the server have extra details?
        var $champ_id;
        var $champ_name;
        var $champ_broadcast;
        var $champ_logo;
        var $event_title;
        var $event_open;
        var $track_name;
        var $track_location;
        var $track_logo;
        var $event_date;
        var $event_id;


	var $hide_server_prefix; // When MOTD is prefixed by this string, the server will be displayed as OFFLINE


	function rFQuery($host, $port, $read_blocked = true)
	{
		$this->ip = gethostbyname($host);
		$this->port = $port;	
		$this->read_blocked = $read_blocked;	

		$this->serveronline = FALSE;
		$this->hide_server_prefix = '';

		$this->gameversion  = 0;
		$this->servername   = "";
		$this->series       = "";
		$this->track        = "";
		$this->session      = SESSION_UNDEF;
		$this->session_time = 0;
		$this->session_laps = 0;
		$this->curplayers   = 0;
		$this->maxplayers   = 0;
		$this->numaiplayers = 0;
		$this->damagemult   = 0;
		$this->datarate     = 0;
	}
	
	function SetHideServerPrefix($prefix)
	{
		$this->hide_server_prefix = $prefix;
	}
	
	function _readFromSocket($socket, $num_bytes, $timeout)
	{
		$response = "";
		
		if ($this->read_blocked)
		{
			socket_set_blocking($socket, true);    
			socket_set_timeout($socket, $timeout, 0);
			
			$response = fread($socket, $num_bytes);			
		}
		else
		{		
			$start = time();		
	
			socket_set_blocking($socket, false);		
		
			do
			{			
				$response .= fread($socket, $num_bytes);
				$got_data = strlen($response) >= $num_bytes;			
			} while (!$got_data && ((time() - $start) < $timeout));
		}

	   return $response;
	}
	

	// Send a command to the server
	function _sendCommand($command, $timeout = 5)
	{
		$response = "";

		// First of all open a socket
		if ($socket = @fsockopen("udp://" . $this->ip, $this->port))
		{
			// Set communication properties
			socket_set_blocking($socket, true);      
                        socket_set_timeout($socket, $timeout, 0);
      
	      // Send the command
	      if (fwrite($socket, $command, strlen($command)) == strlen($command))
	      {
	      	// Read server response
	      	$response = $this->_readFromSocket($socket, 623, $timeout);
	      }
	      else
	      {
	      	// TODO: Error -> the command was not completely written
	      }

	      // Close socket
	      fclose($socket);
		}
		else
		{
			// TODO: Error message
		}
		
		return $response;
	}
	
	function _ByteFromBuffer($buffer, $idx)
	{
		return ord($buffer[$idx]);
	}
	
	
	function _ShortFromBuffer($buffer, $idx)
	{
		$shortval =  ord($buffer[$idx]) | (ord($buffer[$idx+1]) << 8);
	
		return $shortval;
	}


	function _LongFromBuffer($buffer, $idx)
	{
		$longval =  ord($buffer[$idx]) | (ord($buffer[$idx+1]) << 8) | (ord($buffer[$idx+2]) << 16) | (ord($buffer[$idx+3]) << 24);
	
		return $longval;
	}
	
	
	function _StringFromBuffer($buffer, $idx)
	{
		$result = "";
	
		while ($buffer[$idx] != "\x00")
		{
			$result .= $buffer[$idx++];
		}

		return $result;
	}	
	
	// Call this function to connect to the server and retrieve information from it.
	// Must be called before you query the details using the _getXXX/_isXXX methods.
	function _queryServerInfo($timeout = 1, $extrainfo = true)
	{
		$response = "";
		$response = $this->_sendCommand("rF_S\0", $timeout);
		if (strlen($response) > 0)
		{
			$this->gameversion  = $this->_ShortFromBuffer($response, 0x11);
			$this->servername   = $this->_StringFromBuffer($response, 0x2D);
			$this->servermodt   = $this->_StringFromBuffer($response, 0x69);		
			$this->series       = $this->_StringFromBuffer($response, 0x19);
			$this->track        = str_replace(',','',$this->_StringFromBuffer($response, 0x49));
			$this->session      = $this->_ByteFromBuffer($response, 0xD2) >> 5;
			$this->session_time = $this->_ShortFromBuffer($response, 0xD9);
			$this->session_laps = $this->_ShortFromBuffer($response, 0xDB) / 16;
			$this->curplayers   = $this->_ByteFromBuffer($response, 0xCF);
			$this->maxplayers   = $this->_ByteFromBuffer($response, 0xD0);
			$this->numaiplayers = $this->_ByteFromBuffer($response, 0xD1);
			$this->damagemult   = $this->_ByteFromBuffer($response, 0xD3);
			$this->datarate     = $this->_ByteFromBuffer($response, 0xCE);
			
			$prefix_len = strlen($this->hide_server_prefix);
			$this->serveronline = ($prefix_len == 0) || (strncmp($this->servermodt, $this->hide_server_prefix, $prefix_len) != 0);

                        if($extrainfo)
                            $this->getExtraInfo();
		}
		else
		{
			$this->serveronline = FALSE;
		}
		
		return $this->serveronline;
	}

        // Tries to get extra data from the database.
	function getExtraInfo()
        {
            if($this->serveronline)
            {
                global $db;
                $q = "SELECT c_id, c_title, c_broadcastLiveURL, c_logoURL, e_title, e_openSignUps, e_id,
                             t_fullTitle, t_location, t_logo, date_format(ed_date, '%M %e, %Y, %l:%i%p') as ed_datef
                      FROM " . CHAMPIONSHIPS_TABLE . ", " . EVENTS_TABLE . ", " . TRACKS_TABLE . ", " . EVENTDATES_TABLE . "
                      WHERE e_championship = c_id AND e_track = t_id AND ed_event = e_id AND c_rfm = '{$this->series}' AND ed_date > DATE_SUB(NOW(), INTERVAL 1 DAY)
                            AND FIND_IN_SET('{$this->track}',t_eventnames) > 0
                      ORDER BY ed_date ASC
                      LIMIT 0,1";

		$result = $db->sql_query($q);
		if($row = $db->sql_fetchrow($result))
                {
                    $this->hasextrainfo = true;
                    $this->champ_broadcast = $row['c_broadcastLiveURL'];
                    $this->champ_id = $row['c_id'];
                    $this->champ_logo = $row['c_logoURL'];
                    $this->champ_name = $row['c_title'];
                    $this->event_id = $row['e_id'];
                    $this->event_date = $row['ed_datef'];
                    $this->event_open = $row['e_openSignUps'];
                    $this->event_title = $row['e_title'];
                    $this->track_location = $row['t_location'];
                    $this->track_logo = $row['t_logo'];
                    $this->track_name = $row['t_fullTitle'];
                }
            }
        }

	// Determines whether or not the server is online.
	function _isServerOnline()
	{
		return $this->serveronline;
	}
	
	// Return the server address in style: IP:PORT
	function _getFullServerAddr()
	{
		return $this->ip . ":" . $this->port;
	}
	
	// Create a server URL (for use in href so users can click on it and autom. connect to the server)
	function _getServerURL()
	{
		return 'rFactor://' . $this->_getFullServerAddr();
	}
	
	
	function _getGameVersion()
	{
		return $this->gameversion;
	}

	// Retrieve the server name
	function _getServerName()
	{
		return $this->servername;
	}
	
	// Retrieve the servers motto of the day
	function _getServerMOTD()
	{
		return $this->servermodt;
	}

	// Retrieve the name of the race series
	function _getSeries()
	{
		return $this->series;
	}
	
	// Retrieve the name of the current track
	function _getTrack()
	{
		return $this->track;
	}
	
	// Retrieve the active session
	function _getSession()
	{
		return $this->session;
	}
	
	// Retrieve the time remaining for the current session
	function _getSessionTime()
	{
		return $this->session_time;
	}
	
	// Retrieve the remaining laps for the current session
	function _getSessionLaps()
	{
		return $this->session_laps;
	}	
		
	// Retrieve the number of actual drivers
	function _getCurPlayers()
	{
		return $this->curplayers;
	}
	
	// Retrieve the maximum allowed number of drivers
	function _getMaxPlayers()
	{
		return $this->maxplayers;
	}
	
	// Retrieve the number of AI drivers
	function _getNumAiPlayers()
	{
		return $this->numaiplayers;
	}
	
	// Retrieve the damage multiplier (in %)
	function _getDamageMultiplier()
	{
		return $this->damagemult;
	}
	
	// Retrieve data rate of server (in kbps)
	function _getDataRate()
	{
		return $this->datarate;
	}
}

}

?>